//
// Created by Administrator on 2023/10/7.
//

#ifndef NAVIGATION_TILECACHEMEMORY_H
#define NAVIGATION_TILECACHEMEMORY_H

#include "DetourTileCacheBuilder.h"
#include "DetourCommon.h"
#include "DetourTileCache.h"
#include "InputGeom.h"
#include "DetourNavMeshBuilder.h"
#include "Sample.h"
#include "fastlz.h"

struct LinearAllocator : public dtTileCacheAlloc {
    unsigned char *buffer;
    size_t capacity;
    size_t top;
    size_t high;

    LinearAllocator(const size_t cap) : buffer(0), capacity(0), top(0), high(0) {
        resize(cap);
    }

    virtual ~LinearAllocator();

    void resize(const size_t cap) {
        if (buffer) dtFree(buffer);
        buffer = (unsigned char *) dtAlloc(cap, DT_ALLOC_PERM);
        capacity = cap;
    }

    virtual void reset() {
        high = dtMax(high, top);
        top = 0;
    }

    virtual void *alloc(const size_t size) {
        if (!buffer)
            return 0;
        if (top + size > capacity)
            return 0;
        unsigned char *mem = &buffer[top];
        top += size;
        return mem;
    }

    virtual void free(void * /*ptr*/) {
        // Empty
    }
};

struct MeshProcess : public dtTileCacheMeshProcess {
    InputGeom *m_geom;

    inline MeshProcess() : m_geom(0) {
    }

    virtual ~MeshProcess();

    inline void init(InputGeom *geom) {
        m_geom = geom;
    }

    virtual void process(struct dtNavMeshCreateParams *params,
                         unsigned char *polyAreas, unsigned short *polyFlags) {
        // Update poly flags from areas.
        for (int i = 0; i < params->polyCount; ++i) {
            if (polyAreas[i] == DT_TILECACHE_WALKABLE_AREA)
                polyAreas[i] = SAMPLE_POLYAREA_GROUND;

            if (polyAreas[i] == SAMPLE_POLYAREA_GROUND ||
                polyAreas[i] == SAMPLE_POLYAREA_GRASS ||
                polyAreas[i] == SAMPLE_POLYAREA_ROAD) {
                polyFlags[i] = SAMPLE_POLYFLAGS_WALK;
            } else if (polyAreas[i] == SAMPLE_POLYAREA_WATER) {
                polyFlags[i] = SAMPLE_POLYFLAGS_SWIM;
            } else if (polyAreas[i] == SAMPLE_POLYAREA_DOOR) {
                polyFlags[i] = SAMPLE_POLYFLAGS_WALK | SAMPLE_POLYFLAGS_DOOR;
            }
        }

        // Pass in off-mesh connections.
        if (m_geom) {
            params->offMeshConVerts = m_geom->getOffMeshConnectionVerts();
            params->offMeshConRad = m_geom->getOffMeshConnectionRads();
            params->offMeshConDir = m_geom->getOffMeshConnectionDirs();
            params->offMeshConAreas = m_geom->getOffMeshConnectionAreas();
            params->offMeshConFlags = m_geom->getOffMeshConnectionFlags();
            params->offMeshConUserID = m_geom->getOffMeshConnectionId();
            params->offMeshConCount = m_geom->getOffMeshConnectionCount();
        }
    }
};

struct FastLZCompressor : public dtTileCacheCompressor {
    virtual ~FastLZCompressor();

    virtual int maxCompressedSize(const int bufferSize) {
        return (int) (bufferSize * 1.05f);
    }

    virtual dtStatus compress(const unsigned char *buffer, const int bufferSize,
                              unsigned char *compressed, const int /*maxCompressedSize*/, int *compressedSize) {
        *compressedSize = fastlz_compress((const void *const) buffer, bufferSize, compressed);
        return DT_SUCCESS;
    }

    virtual dtStatus decompress(const unsigned char *compressed, const int compressedSize,
                                unsigned char *buffer, const int maxBufferSize, int *bufferSize) {
        *bufferSize = fastlz_decompress(compressed, compressedSize, buffer, maxBufferSize);
        return *bufferSize < 0 ? DT_FAILURE : DT_SUCCESS;
    }
};

#endif //NAVIGATION_TILECACHEMEMORY_H
